package com.vlefen.springcloud.grpc.config;

import com.google.common.collect.Sets;
import com.vlefen.springcloud.grpc.Registry;
import com.vlefen.springcloud.grpc.GRpc;
import com.vlefen.springcloud.grpc.consul.ConsulRegistry;
import com.vlefen.springcloud.grpc.grpc.GRpcServer;
import com.vlefen.springcloud.grpc.grpc.RpcServer;
import io.grpc.BindableService;
import io.grpc.ServerServiceDefinition;
import io.grpc.netty.NettyServerBuilder;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties;
import org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistry;
import org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistryAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Set;


@Configuration
@AutoConfigureAfter(ConsulServiceRegistryAutoConfiguration.class)
public class GRpcServerAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(NettyServerBuilder.class)
    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    public RpcServer rpcServer(@Qualifier(Constants.GRPC_SERVER_SERVICE_DEFINITION_BEAN_NAME)
                                       Set<ServerServiceDefinition> serviceDefinitions,
                               GRpcProperties gRpcProperties) {
        return new GRpcServer(serviceDefinitions, gRpcProperties);
    }


    @Bean
    @ConditionalOnMissingBean
    @Qualifier(Constants.GRPC_SERVER_SERVICE_DEFINITION_BEAN_NAME)
    public Set<ServerServiceDefinition> findServerService(ApplicationContext applicationContext) {
        String[] serviceBeans = applicationContext.getBeanNamesForAnnotation(GRpc.class);
        Set<ServerServiceDefinition> serviceDefinitions = Sets.newHashSet();
        for (String serviceBean : serviceBeans) {
            BindableService bindableService = applicationContext.getBean(serviceBean, BindableService.class);
            ServerServiceDefinition serviceDefinition = bindableService.bindService();
            serviceDefinitions.add(serviceDefinition);
        }
        return serviceDefinitions;
    }


    @Bean
    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @ConditionalOnClass({ConsulServiceRegistry.class, ConsulDiscoveryProperties.class})
    public Registry consulRegistry(
            ConsulServiceRegistry consulServiceRegistry,
            ApplicationContext context,
            ConsulDiscoveryProperties discoveryProperties,
            GRpcProperties gRpcProperties,
            Set<ServerServiceDefinition> serverServiceDefinitions
    ) {

        return new ConsulRegistry(serverServiceDefinitions, consulServiceRegistry, discoveryProperties, gRpcProperties, context);
    }

    @Bean
    public SmartLifecycle serverSmartLifecycle(RpcServer rpcServer, Registry registry) {
        return new SmartLifecycle() {
            @Override
            public boolean isAutoStartup() {
                return true;
            }

            @Override
            public void stop(Runnable runnable) {
                rpcServer.stop();
                runnable.run();
            }

            @Override
            public void start() {
                rpcServer.start();
                registry.register();
            }

            @Override
            public void stop() {
                rpcServer.stop();
                registry.deregister();
            }

            @Override
            public boolean isRunning() {
                return rpcServer.running();
            }

            @Override
            public int getPhase() {
                return Integer.MAX_VALUE;
            }
        };
    }
}
